package puzzle.game.ui;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.newdawn.slick.Color;
import org.newdawn.slick.Font;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.gui.BasicComponent;
import org.newdawn.slick.gui.ComponentListener;
import org.newdawn.slick.gui.GUIContext;

/** 
 * A simple click button used in the puzzle engine.
 *
 * @author davedes
 */
public class PuzzleButton extends BasicComponent {
  
  /** The UP state of this button (ie: still). */
  public static final int UP = 0;
  
  /** The DOWN state of this button (ie: pressed). */
  public static final int DOWN = 1;
  
  /** The ROLLOVER state of this button (ie: mouse hover). */
  public static final int ROLLOVER = 2;
  
  /** The x position of this component. */
  private int x=0;
  
  /** The y position of this component. */
  private int y=0;
  
  /** The text for the button. */
  private String text;
  
  /** The font to draw with. */
  private Font font;
  
  /** Whether the mouse is inside this component (rollover). */
  private boolean inside = false;
  
  /** The current state of the button. */
  private int state = UP;
  
  /** The width and height of the button. */
  private int width=0,height=0;
  
  /** Colors for the different states. */
  private Color up=Color.black, down=Color.black, roll=Color.black;
  
  /** A list of component listeners which receive events on click. */
  private List<ComponentListener> listeners = new ArrayList<ComponentListener>();
  
  /** Creates the button with the specified font and text. */
  public PuzzleButton(GameContainer c, Font font, String text) {
    super(c);
    this.font = font;
    this.text = text;
    this.width = font.getWidth(text);
    this.height = font.getHeight(text);
  }
  
  /** Creates the button with the GameContainer's default font and text. */
  public PuzzleButton(GameContainer c, String text) {
    this(c, c.getDefaultFont(), text);
  }
  
  /** Used internally to render the component. */
  public void renderImpl(GUIContext c, Graphics g) {
    Font fOld = g.getFont();
    Color cOld = g.getColor();
    
    
    g.setFont(font);
    if (state==DOWN) {
      renderDown(g);
    }
    else if (state==ROLLOVER) {
      renderRollover(g);
    }
    else {
      renderUp(g);
    }
    g.setFont(fOld);
    g.setColor(cOld);
  }
  
  /** Used internally to render the DOWN state. */
  public void renderDown(Graphics g) {
    g.setColor(down);
    g.drawString(text, x, y);
  }
  
  /** Used internally to render the UP state. */
  public void renderUp(Graphics g) {
    g.setColor(up);
    g.drawString(text, x, y);
  }
  
  /** Used internally to render the ROLLOVER state. */
  public void renderRollover(Graphics g) {
    g.setColor(roll);
    g.drawString(text, x, y);
  }
  
  /** Gets the width of this component. */
  public int getWidth() {
    return width;
  }
  
  /** Gets the height of this component. */
  public int getHeight() {
    return height;
  }
  
  /** Sets the x position. */
  public void setX(int x) {
    this.x = x;
  }
  
  /** Sets the y position. */
  public void setY(int y) {
    this.y = y;
  }
  
  /** Gets the x position. */
  public int getX() {
    return x;
  }
  
  /** Gets the y position. */
  public int getY() {
    return y;
  }
  
  /** Sets the location. */
  public void setLocation(int x, int y) {
    setX(x);
    setY(y);
  }
  
  /** Sets the text. */
  public void setText(String text) {
    this.text = text;
    this.width = font.getWidth(text);
    this.height = font.getHeight(text);
  }
  
  /** Sets the font to draw with. */
  public void setFont(Font font) {
    this.font = font;
    this.width = font.getWidth(text);
    this.height = font.getHeight(text);
  }
  
  /** Sets the color to draw when in the specified state. */
  public void setColor(int state, Color bg) {
    switch (state) {
      case UP: up=bg; break;
      case DOWN: down=bg; break;
      case ROLLOVER: roll=bg; break;
    }
  }
  
  /** Adds a ComponentListener to the list which can receive button-click events. */
  public void addComponentListener(ComponentListener l) {
    listeners.add(l);
  }
  
  /** Removes a ComponentListener from the list. */
  public void removeComponentListener(ComponentListener l) {
    listeners.remove(l);
  }
  
  /** Dispatches the component activated event. */
  protected void dispatchEvent() {
    for (ComponentListener l : listeners) {
      l.componentActivated(this);
    }
  }
  
  /** Used internally to determine whether the mouse is over this component. */
  private boolean isMouseOver(int nx, int ny) {
    return nx>=x && nx<=( x+width )
                && ny>=y && ny<= ( y+height );
  }
  
  /** Called when the mouse is moved. */
  public void mouseMoved(int ox, int oy, int nx, int ny) {
    inside = isMouseOver(nx, ny);
    
    //mouse entered
    //don't change state if it's down
    if (inside)
      state = (state==DOWN) ? DOWN : ROLLOVER;
    //mouse exited
    //don't change state if it's down
    else if (state!=UP)
      state = (state==DOWN) ? DOWN : UP;
  }
  
  /** Called when the mouse is pressed. */
  public void mousePressed(int button, int x, int y) {
    //if mouse is over and we clicked the first button
    if (button==0&&isMouseOver(x,y)) {
      state = DOWN;
    }
  }
  
  /** Called when the mouse is released. */
  public void mouseReleased(int button, int x, int y) {
    //Set state back to UP unless the mouse is over,
    //if it is then set it to ROLLOVER.
    state = (inside) ? ROLLOVER : UP;
    
    //only dispatch if we've released inside of the component
    if (inside)
      dispatchEvent();
  }

}
